import math
import sys
import numpy as np
from matplotlib import pyplot as plt


def max(a, b): return a if a > b else b


def min(a, b): return a if a < b else b


def clamp(n, smallest, largest):
    return max(smallest, min(n, largest))


def Lerp(A, B, Alpha):
    return A + Alpha * (B-A)


def InterpEaseIn(A, B, Alpha, Exp):
    ModifiedAlpha = math.pow(Alpha, Exp)
    return Lerp(A, B, ModifiedAlpha)


def InterpEaseOut(A, B, Alpha, Exp):
    ModifiedAlpha = 1 - math.pow(1 - Alpha, Exp)
    return Lerp(A, B, ModifiedAlpha)


def InterpEaseInOut(A, B, Alpha, Exp):
    return Lerp(A, B, InterpEaseIn(0, 1, Alpha * 2, Exp) * 0.5
                if Alpha < 0.5 else InterpEaseOut(0, 1, Alpha * 2 - 1, Exp) * 0.5 + 0.5)


def InterpStep(A, B, Alpha, Steps):
    if Steps <= 1 or Alpha <= 0:
        return A
    elif Alpha >= 1:
        return B

    NumIntervals = Steps - 1
    ModifiedAlpha = math.floor(Alpha * Steps) / NumIntervals
    return Lerp(A, B, ModifiedAlpha)


def InterpSinIn(A, B, Alpha):
    ModifiedAlpha = -1 * math.cos(Alpha * math.pi / 2) + 1
    return Lerp(A, B, ModifiedAlpha)


def InterpSinOut(A, B, Alpha):
    ModifiedAlpha = math.sin(Alpha * math.pi / 2)
    return Lerp(A, B, ModifiedAlpha)


def InterpExpoIn(A, B, Alpha):
    ModifiedAlpha = 0 if Alpha == 0 else math.pow(2, 10 * (Alpha - 1))
    return Lerp(A, B, ModifiedAlpha)


def InterpCircularIn(A, B, Alpha):
    print(Alpha * Alpha)
    ModifiedAlpha = -1 * (math.sqrt(1 - Alpha * Alpha) - 1)
    return Lerp(A, B, ModifiedAlpha)


def InterpTo(Current, Target, DeltaTime, InterpSpeed):
    Dist = Target - Current
    if Dist * Dist < 0.0001:
        return Target

    DeltaMove = Dist * clamp(DeltaTime * InterpSpeed, 0, 1)
    return Current + DeltaMove


def InterpToConstnatTo(Current, Target, DeltaTime, InterpSpeed):
    Dist = Target - Current
    if Dist * Dist < 0.0001:
        return Target

    Step = InterpSpeed * DeltaTime
    return Current + clamp(Dist, - Step, Step)


def SmoothStep(A, B, X):
    if X < A:
        return 0
    elif X > B:
        return 1
    InterpFraction = (X - A) / (B - A)
    return InterpFraction * InterpFraction * (3 - 2 * InterpFraction)

################################################################


A = 0
B = 100

AlphaArray = np.arange(0, 1.05, 0.05)

ArgNameList_Interp_Exp = ['A', 'B', 'Alpha', 'Exp']
ExpArray = [2, 5, 10]

ArgNameList_Interp_Step = ['A', 'B', 'Alpha', 'Steps']
StepArray = np.arange(5, 7, 1)

# --------------------
ArgNameList_InterpTo = ['Current', 'Target', 'DeltaTime', 'InterpSpeed']
Current = 1
Target = 100
InterpSpeed = 10

ArgNameList_SmoothStep = ['A', 'B', 'X']

###
CurrentCallFuncName = 'SmoothStep'


################################################################

plt.title(CurrentCallFuncName)
plt.xlabel("Alpha")
plt.ylabel("Value")


module_name = sys.modules['__main__']
called_function = getattr(module_name, CurrentCallFuncName)


def CheckFunctionArg(ArgNames: list):
    ArgList = called_function.__code__.co_varnames
    for ArgName in ArgNames:
        if ArgName not in ArgList:
            return False
    return True


if CheckFunctionArg(ArgNameList_Interp_Exp):
    for Exp in ExpArray:
        ValueArray = []
        for i in AlphaArray:
            ValueArray.append(called_function(A, B, i, Exp))

        plt.plot(AlphaArray, ValueArray, label='Exp = ' + str(Exp))
elif CheckFunctionArg(ArgNameList_Interp_Step):
    for Step in StepArray:
        ValueArray = []
        for i in AlphaArray:
            ValueArray.append(called_function(A, B, i, Step))
        plt.plot(AlphaArray, ValueArray, label='Step = ' + str(Step))
elif CheckFunctionArg(ArgNameList_InterpTo):
    TempCurrent = Current
    Time = 0
    DtTime = 1 / 60
    XArray = []
    YArray = []
    plt.ylabel("current value")
    plt.xlabel("total time")
    while TempCurrent != Target:
        TempCurrent = called_function(TempCurrent, Target, DtTime, InterpSpeed)
        Time += DtTime
        XArray.append(Time)
        YArray.append(TempCurrent)
    plt.plot(XArray, YArray)

elif CheckFunctionArg(ArgNameList_SmoothStep):
    AlphaArray = [-100, 0, 10, 20, 30, 40, 50, 60, 70, 80,90, 100, 110]
    ABValue = [(40, 80), (0, 100)]
    plt.ylabel("Value")
    plt.xlabel("X")
    for ab in ABValue :
        ValueArray = []
        for i in AlphaArray:
            ValueArray.append(called_function(ab[0], ab[1], i))
        plt.plot(AlphaArray, ValueArray, label = 'A = ' + str(ab[0]) + ' B = ' + str(ab[1]))

else:
    ValueArray = []
    for i in AlphaArray:
        ValueArray.append(called_function(A, B, i))
    plt.plot(AlphaArray, ValueArray)


# ValueArray2 = []
# for i in AlphaArray:
#    ValueArray2.append((math.sqrt(i * i)))
# plt.plot(AlphaArray, ValueArray2)

plt.legend()
plt.show()
